<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="Sm2RU" id="Sm2RU"><span data-lake-id="u1bdccd5f" id="u1bdccd5f">典型回答</span></h1>
  <p data-lake-id="u86d708ff" id="u86d708ff"><span data-lake-id="u2115d100" id="u2115d100">volatile通常被比喻成”轻量级的synchronized“，也是Java并发编程中比较重要的一个关键字。和synchronized不同，volatile是一个变量修饰符，只能用来修饰变量。无法修饰方法及代码块等。</span></p>
  <p data-lake-id="ucc1cf374" id="ucc1cf374"><span data-lake-id="u4c89519f" id="u4c89519f">​</span><br></p>
  <p data-lake-id="ufff803a4" id="ufff803a4"><span data-lake-id="ufee176cb" id="ufee176cb">volatile的用法比较简单，只需要在声明一个可能被多线程同时访问的变量时，使用volatile修饰就可以了。</span></p>
  <p data-lake-id="ub5fab355" id="ub5fab355"><span data-lake-id="u7b8b0650" id="u7b8b0650">​</span><br></p>
  <p data-lake-id="u3d0da421" id="u3d0da421"><span data-lake-id="u76481e49" id="u76481e49">但是， volatile在线程安全方面，可以保证有序性和可见性，但是是不能保证原子性的 。</span></p>
  <p data-lake-id="u2365dc53" id="u2365dc53"><span data-lake-id="u8e510c7c" id="u8e510c7c">​</span><br></p>
  <p data-lake-id="u4d6c4ba9" id="u4d6c4ba9"><span data-lake-id="u0e4f1111" id="u0e4f1111">synchronized可以保证原子性 ，因为被synchronized修饰的代码片段，在进入之前加了锁，只要他没执行完，其他线程是无法获得锁执行这段代码片段的，就可以保证他内部的代码可以全部被执行。进而保证原子性。</span></p>
  <p data-lake-id="u2ff5c6ee" id="u2ff5c6ee"><span data-lake-id="u45a70bff" id="u45a70bff">​</span><br></p>
  <p data-lake-id="u20d674bc" id="u20d674bc"><span data-lake-id="ud263f3ff" id="ud263f3ff">那么，</span><strong><span data-lake-id="ub2ad09f1" id="ub2ad09f1">为什么volatile不能保证原子性呢？因为他不是锁，他没做任何可以保证原子性的处理。当然就不能保证原子性了。</span></strong></p>
  <p data-lake-id="u05b251c0" id="u05b251c0"><br></p>
  <p data-lake-id="uf6d616d3" id="uf6d616d3"><br></p>
  <p data-lake-id="u8bea9021" id="u8bea9021"><span data-lake-id="u1a035cab" id="u1a035cab">比如有如下代码：</span></p>
  <p data-lake-id="ud174aaf2" id="ud174aaf2"><span data-lake-id="u9012d7f0" id="u9012d7f0">​</span><br></p>
  <pre lang="java"><code>
public class Test {

    volatile int number = 0;

    public void increase() {
        number++;
    }

    public static void main(String[] args) {
        Test volatileAtomDemo = new Test();
        for (int j = 0; j &lt; 10; j++) {
            new Thread(() -&gt; {
                for (int i = 0; i &lt; 1000; i++) {
                    volatileAtomDemo.increase();
                }
            }, String.valueOf(j)).start();
        }
        while (Thread.activeCount() &gt; 2) {
            Thread.yield();
        }

        System.out.println(Thread.currentThread().getName() +
                           " final number result = " + volatileAtomDemo.number);
    }

}
</code></pre>
  <p data-lake-id="uabe11a64" id="uabe11a64"><br></p>
  <blockquote data-lake-id="u4b6cbaba" id="u4b6cbaba">
   <p data-lake-id="ude0e190a" id="ude0e190a"><span data-lake-id="u34791389" id="u34791389">Thread.yield()方法被用来暂停当前正在执行的线程，以允许其他线程获得执行机会，以此来提高并发竞争。</span></p>
  </blockquote>
  <p data-lake-id="u83fa9ba0" id="u83fa9ba0"><span data-lake-id="uf986956b" id="uf986956b">​</span><br></p>
  <p data-lake-id="ub980248b" id="ub980248b"><span data-lake-id="u8747a5f0" id="u8747a5f0">以上程序，正常情况下，输出结果应该是10000，但是真正执行的话就会发现，没办法保证每次执行结果都是10000，这就是因为i++这个操作没办法保证他是原子性的。</span></p>
  <p data-lake-id="u7f83ca1b" id="u7f83ca1b"><span data-lake-id="u8af83aba" id="u8af83aba">i++被拆分成3个指令：</span></p>
  <ul list="ue267e3a3">
   <li fid="ue61ca078" data-lake-id="uc96f9b08" id="uc96f9b08"><span data-lake-id="u2af9fd6e" id="u2af9fd6e">执行GETFIELD拿到主内存中的原始值number。</span></li>
   <li fid="ue61ca078" data-lake-id="u33781a71" id="u33781a71"><span data-lake-id="u996145fb" id="u996145fb">执行IADD进行加1操作。</span></li>
   <li fid="ue61ca078" data-lake-id="u51157469" id="u51157469"><span data-lake-id="u6fb64222" id="u6fb64222">执行PUTFIELD把工作内存中的值写回主内存中。</span></li>
   <li fid="ue61ca078" data-lake-id="uc3b3a21a" id="uc3b3a21a"><span data-lake-id="u3834ae5c" id="u3834ae5c">​</span><br></li>
  </ul>
  <p data-lake-id="u577c9afb" id="u577c9afb"><span data-lake-id="u806a3181" id="u806a3181">当多个线程并发执行PUTFIELD指令的时候，会出现写回主内存覆盖问题，所以才会导致最终结果不为 10000，所以 volatile 不能保证原子性。</span></p>
  <p data-lake-id="u57d0e49f" id="u57d0e49f"><span data-lake-id="uaca0c764" id="uaca0c764">​</span><br></p>
  <h1 data-lake-id="GL3fF" id="GL3fF"><span data-lake-id="u9665336c" id="u9665336c">扩展知识</span></h1>
  <p data-lake-id="uc9d2e0ca" id="uc9d2e0ca"><br></p>
  <h2 data-lake-id="uZIXv" id="uZIXv"><span data-lake-id="u23ab9f32" id="u23ab9f32">如何实现原子性的自增</span></h2>
  <p data-lake-id="ub4613658" id="ub4613658"><br></p>
  <p data-lake-id="u00c4f71e" id="u00c4f71e"><span data-lake-id="ucd9dd9a6" id="ucd9dd9a6">因为volatile没办法保证原子性，在并发场景中的i++方法会有并发问题，那么有一种解决方案就是使用支持原子性操作的数字类型。如AtomicLong、AtomicInteger等。</span></p>
  <p data-lake-id="u3343e350" id="u3343e350"><br></p>
  <p data-lake-id="u06fc7191" id="u06fc7191"><span data-lake-id="udcc0c305" id="udcc0c305">​</span><br></p>
 </body>
</html>